Please enable JavaScript to view this website.

Skip to main content
Experimental proof of concept

Edge Mesh is a throwaway proof-of-concept / learning experiment built on top of the Greengrass reference implementation. It is not a supported or maintained deliverable and may be removed. The reference implementation itself is long-lived; Edge Mesh is not.

Hardware & RF

In a nutshell

Every mesh radio is an ath9k_htc USB dongle, because a hard driver interop wall means mixed radios (Intel or Broadcom) silently won't carry the mesh. On the Pis the dongle is plug-and-play; on the CompuLab core the ath9k_htc and batman-adv kernel modules must be cross-built against the exact running kernel and side-loaded. Every node must share identical RF constants (SSID, channel 1, fixed BSSID) or it forms its own isolated cell. Before this: skim the Overview.

This page is for electrical and firmware engineers. It covers the physical nodes, the radio decision that makes the mesh work for us, the RF constants every node must share, and the kernel-module constraints that make the CompuLab core the hard part of the build.

The lessons here were won the hard way; the authoritative deep-dive with the exact commands lives in tools/mesh/COMPULAB-IMX8.md.

The hardware

NodeHardwareOSMesh radio
CoreCompuLab IOT-GATE-iMX8 (i.MX 8M Mini)Debian Bookworm, aarch64ath9k_htc USB dongle (onboard Intel wlan0 stays on corp WiFi)
Mesh-nodeRaspberry Pi 4Raspberry Pi OS (Trixie)ath9k_htc USB dongle (onboard Broadcom wlan0 stays free for management)

The mesh radio on every node is an ath9k_htc USB WiFi dongle, an Atheros AR9271, e.g. the Alfa AWUS036NHA or a TP-Link TL-WN722N v1 only (v2/v3 are Realtek and will not work). It typically enumerates as wlan1. The onboard radio is deliberately left off the mesh and stays available for management WiFi.

The ath9k_htc dongle decision

The decision to standardize on ath9k_htc dongles was not a preference. It was forced by a hard interop wall (below) and two radio traps.

The Intel radio trap (core)

The IOT-GATE-iMX8's onboard wlan0 is an Intel WiFi card (iwlwifi). Intel's iwlwifi advertises IBSS in iw phy but its IBSS is non-functional for broadcast/multicast:

  • Nodes join the cell and see each other at layer 2 (beacons cross; station dump shows the peer).
  • But batman OGMs (broadcast data frames) never cross in either direction, so batctl n stays empty forever.

No software toggle fixes this, and the card has no 802.11s "mesh point" mode either, so there is no fallback. iw phy advertising * IBSS is necessary but not sufficient: only running batman and checking batctl n proves a radio works.

The interop wall: brcmfmacath9k_htc

Even with an Atheros dongle on the core and the Pi's onboard Broadcom (brcmfmac) radio on the mesh, the mesh silently fails:

An ath9k_htc dongle and a Pi's brcmfmac radio do not carry IBSS broadcast/multicast data between each other. Beacons cross (L2 association looks healthy), but batman OGM / broadcast data frames never do, in either direction. It is a driver interop wall, not a tunable knob.

Two ath9k_htc radios mesh instantly. The fix was therefore homogeneous hardware: make every mesh radio an ath9k_htc dongle. On the Pis the dongle is plug-and-play (Raspberry Pi OS ships ath9k_htc + htc_9271.fw); on the CompuLab core the driver must be cross-built (below).

Dead ends ruled out during troubleshooting

The interop wall was not fixed by forcing DSSS basic rates (brcmfmac is FullMAC and ignores iw … basic-rates), by TX-power or regulatory tweaks (country 00 already allows 20 dBm IBSS on ch 1), or by swapping dongles (a second AR9271 failed identically). The routing algorithm is BATMAN_IV on both.

The verified cell configuration is OFDM basic-rates 6,12,24 + HT20, wired through mesh-defaults.conf (MESH_BASIC_RATES="6,12,24") and the HT20 token in the IBSS service template.

The RF constants (every node shares these)

Every node must use identical values or it forms its own isolated cell and sees no one. These constants live in tools/mesh/mesh-defaults.conf.

ItemValue
Mesh cell (SSID)edge-mesh
Frequency / channel2412 MHz (channel 1)
BSSID (fixed)02:12:ED:6E:00:01
ModeIBSS ad-hoc (802.11s "mesh point" as fallback only)
Cell ratesOFDM 6,12,24 + HT20
Subnet10.88.0.0/24
Core (reserved)10.88.0.1
mesh-node-N10.88.0.(N+2) (node-0 → .2, … node-4 → .6)

The mesh IP auto-derives from the hostname, so getting the hostname exact at imaging time (mesh-node-N) is what pins each node's address.

Kernel-module constraints (the CompuLab core)

On the Pi, both ath9k_htc and batman-adv ship ready to load. On the CompuLab core, neither does, and they cannot be installed the easy way:

  • Debian dropped batman-adv-dkms (no DKMS shortcut).
  • CompuLab ships the kernel as flat files under /boot with no matching linux-headers and no apt repo.
  • You cannot swap in a stock Debian arm64 kernel: the i.MX 8M Mini WiFi/DT support is vendor-specific.
  • The AR9271 driver was compiled out (# CONFIG_ATH9K_HTC is not set), so plugging in the dongle enumerates on USB but no wlan1 appears, nothing bound.

So both modules must be cross-built against the exact running kernel and side-loaded:

Key constraints the builder handles:

  • CONFIG_MODVERSIONS=y: symbol CRCs are enforced, so a random out-of-tree build will not load. You must build the full kernel from the exact source + config so the CRCs line up. (CONFIG_MODULE_SIG is not set, so unsigned modules load, no signing key needed.)
  • Version-string exactness: the module's vermagic must match uname -r exactly, so the builder pins LOCALVERSION and disables git auto-versioning to avoid the "exceeds 64 characters" doubling trap.
  • Kernel-agnostic interop: batman-adv interoperates as long as the compatibility version (15) and routing algorithm (BATMAN_IV) match. The Pis run batman-adv 2024.2, the iMX8 build is 2023.3, but both are compat 15, so they mesh fine.
  • The ath9k_htc build also needs the firmware on the device (firmware-ath9k-htc, provides htc_9271.fw): the radio will not come up without it.

Where the code lives